Hooking

In computer programming, the term hooking covers a range of techniques used to alter or augment the behavior of an operating system, of applications, or of other software components by intercepting function calls or messages or events passed between software components. Code that handles such intercepted function calls, events or messages is called a "hook".

Hooking is used for many purposes, including debugging and extending functionality. Examples might include intercepting keyboard or mouse event messages before they reach an application, or intercepting operating system calls in order to monitor behavior or modify the function of an application or other component.

Hooking can also be used by malicious code. For example, rootkits, pieces of software that try to make themselves invisible by faking the output of API calls that would otherwise reveal their existence, often use hooking techniques. A wallhack is another example of malicious behavior that can stem from hooking techniques. It is done by intercepting function calls in a computer game and altering what is shown to the player to allow them to gain an unfair advantage over other players.

Contents

Methods

Typically hooks are inserted while software is already running, but hooking is a tactic that can also be employed prior to the application being started. Both these techniques are described in greater detail below.

Physical modification

By physically modifying an executable or library before an application is running through techniques of reverse engineering you can also achieve hooking. This is typically used to intercept function calls to either monitor or replace them entirely.

For example, by using a disassembler, the entry point of a function within a module can be found. It can then be altered to instead dynamically load some other library module and then have it execute desired methods within that loaded library. If applicable, another related approach by which hooking can be achieved is by altering the import table of an executable. This table can be modified to load any additional library modules as well as changing what external code is invoked when a function is called by the application.

An alternate method for achieving function hooking is by intercepting function calls through a wrapper library. When creating a wrapper, you make your own version of a library that an application loads, with all the same functionally of the original library that it will replace. That is, all the functions that are accessible are essentially the same between the original and the replacement. This wrapper library can be designed to call any of the functionality from the original library, or replace it with an entirely new set of logic.

Runtime modification

Operating systems and software may provide the means to easily insert event hooks at runtime. It is available provided that the process inserting the hook is granted enough permission to do so. Microsoft Windows for example, allows you to insert hooks that can be used to process or modify system events and application events for dialogs, scrollbars, and menus as well as other items. It also allows a hook to insert, remove, process or modify keyboard and mouse events. Linux, provides another example where hooks can be used in a similar manner to process network events within the kernel through NetFilter.

When such functionality is not provided, a special form of hooking employs intercepting the library functions calls made by a process. Function hooking is implemented by changing the very first few code instructions of the target function to jump to an injected code. Alternatively on systems using the shared library concept, the interrupt vector table or the import descriptor table can be modified in memory. Essentially these tactics employ the same ideas as those of physical modification, but instead altering instructions and structures located in the memory of a process once it is already running.

Sample code

C# keyboard event hook

The following example will hook into keyboard events in Microsoft Windows using the Microsoft .NET Framework.

using System.Runtime.InteropServices;
 
namespace Hooks
{
	public class KeyHook
	{
		/* Member variables */
		protected static int Hook;
		protected static LowLevelKeyboardDelegate Delegate;
		protected static readonly object Lock = new object();
		protected static bool IsRegistered = false;
 
		/* DLL imports */
		[DllImport("user32")]
		private static extern int SetWindowsHookEx(int idHook, LowLevelKeyboardDelegate lpfn,
			int hmod, int dwThreadId);
 
		[DllImport("user32")]
		private static extern int CallNextHookEx(int hHook, int nCode, int wParam, KBDLLHOOKSTRUCT lParam);
 
		[DllImport("user32")]
		private static extern int UnhookWindowsHookEx(int hHook);
 
		/* Types & constants */
		protected delegate int LowLevelKeyboardDelegate(int nCode, int wParam, ref KBDLLHOOKSTRUCT lParam);
		private const int HC_ACTION = 0;
		private const int WM_KEYDOWN = 0x0100;
		private const int WM_KEYUP = 0x0101;
		private const int WH_KEYBOARD_LL = 13;
 
		[StructLayout(LayoutKind.Sequential)]
		public struct KBDLLHOOKSTRUCT
		{
			public int vkCode;
			public int scanCode;
			public int flags;
			public int time;
			public int dwExtraInfo;
		}
 
		/* Methods */
		static private int LowLevelKeyboardHandler(int nCode, int wParam, ref KBDLLHOOKSTRUCT lParam)
		{
			if (nCode == HC_ACTION)
			{
				if (wParam == WM_KEYDOWN)
					System.Console.Out.WriteLine("Key Down: " + lParam.vkCode);
				else if (wParam == WM_KEYUP)
					System.Console.Out.WriteLine("Key Up: " + lParam.vkCode);
			}
			return CallNextHookEx(Hook, nCode, wParam, lParam);
		}
 
		public static bool RegisterHook()
		{
			lock (Lock)
			{
				if (IsRegistered)
					return true;
				Delegate = LowLevelKeyboardHandler;
				Hook = SetWindowsHookEx(
					WH_KEYBOARD_LL, Delegate,
					Marshal.GetHINSTANCE(
						System.Reflection.Assembly.GetExecutingAssembly().GetModules()[0]
					).ToInt32(), 0
				);
 
				if (Hook != 0)
					return IsRegistered = true;
				Delegate = null;
				return false;
			}
		}
 
		public static bool UnregisterHook()
		{
			lock (Lock)
			{
				return IsRegistered = (UnhookWindowsHookEx(Hook) != 0);
			}
		}
	}
}

Hooking Direct3D

The following is an example of hooking Direct3D function calls on Windows. This utilizes a free hooking library called APIHijack. The source is compiled into a DLL. An additional application that will invoke InstallHook() is also required. For more information see [1]

#include <windows.h>
#include <shlwapi.h>
#include <ddraw.h>
#include "testdll.h"
#include "..\apihijack.h"
 
char temp[256];
HINSTANCE hDLL;
 
// type defs
typedef HRESULT (WINAPI *DirectDrawCreateEx_Type)( GUID FAR *lpGUID, LPVOID *lplpDD, REFIID iid, IUnknown FAR *pUnkOuter );
 
// function prototypes
HRESULT WINAPI MyDirectDrawCreateEx( GUID FAR * lpGuid, LPVOID  *lplpDD, REFIID  iid,IUnknown FAR *pUnkOuter );
 
// hook structure
enum
{
    D3DFN_DirectDrawCreateEx = 0
};
 
SDLLHook D3DHook = 
{
    "DDRAW.DLL",
    false, NULL,
    {
        { "DirectDrawCreateEx", MyDirectDrawCreateEx },
        { NULL, NULL }
    }
};
 
// Hook function.
HRESULT WINAPI MyDirectDrawCreateEx(GUID FAR* lpGuid, LPVOID  *lplpDD, REFIID  iid,IUnknown FAR *pUnkOuter )
{
    DirectDrawCreateEx_Type OldFn = (DirectDrawCreateEx_Type)D3DHook.Functions[D3DFN_DirectDrawCreateEx].OrigFn;
    return OldFn( lpGuid, lplpDD, iid, pUnkOuter );
}
 
// CBT Hook-style injection.
BOOL APIENTRY DllMain( HINSTANCE hModule, DWORD fdwReason, LPVOID lpReserved )
{
    if ( fdwReason == DLL_PROCESS_ATTACH )  // When initializing....
    {
        hDLL = hModule;
 
        // Only hook the APIs if this is the fsim proess.
        GetModuleFileName(GetModuleHandle(NULL), temp, sizeof(temp));
        PathStripPath(temp);
 
        if(stricmp(temp, "fsim.exe" ) == 0)
            HookAPICalls( &D3DHook );
    }
 
    return TRUE;
}
 
// This segment must be defined as SHARED in the .DEF
#pragma data_seg (".HookSection")		
// Shared instance for all processes.
HHOOK hHook = NULL;	
#pragma data_seg ()
 
TESTDLL_API LRESULT CALLBACK HookProc(int nCode, WPARAM wParam, LPARAM lParam) 
{
    return CallNextHookEx( hHook, nCode, wParam, lParam); 
}
 
TESTDLL_API void InstallHook()
{
    hHook = SetWindowsHookEx(WH_CBT, HookProc, hDLL, 0); 
}
 
TESTDLL_API void RemoveHook()
{
    UnhookWindowsHookEx( hHook );
}

API/Function Hooking/Interception Using JMP Instruction

The following source code is an example of API/function hooking using the open source Chrom Library, which hooks by overwriting the first six bytes of a destination function with a JMP instruction to a new function. The code is compiled into a DLL file then loaded into the target process using any method of code injection. This example code hooks the "PR_Write" function of nspr4.dll (used in Firefox to write to a file description) and logs all HTTP/HTTPS requests to file "c:/log.txt". For more information, see [2].

/*
Firefox_hook (C) Raja Jamwal 2011
Distributed under GNU GPL License
 
Firefox_hook is a example code for Chrom Library, Firefox_hook log every
HTTP/HTTPS requests that firefox makes
 
Chrom, is API/Funtion interception/hijacking library for windows systems
Copyright (C) 2011  Raja Jamwal
 
This file is part of Chrom.
*/
 
#include "stdafx.h"
#include "chrom.h"
 
Hook Firefox; // Hook firefox
DWORD PR_Write_H (DWORD *fd,  void *buf,DWORD amount); // this is our overiding-function
typedef DWORD (*prWrite)(DWORD*,void*,DWORD); // defination of our original function
 
prWrite prw = NULL; // create a original function, we later point this to original function
		    // address
 
char log_file[]="c://log.txt"; // logfile
 
// this will log our data
int write_log(char * log, char * data)
{
ofstream fout(log, ios::app);
fout << data;
fout.close();
return TRUE;
}
 
// initialize hooking, this adds the jump instruction to original function address
int create_hooks()
{
	// Override PR_Write function in nspr4.dll with our PR_Write_H, 
	// Note nspr4.dll must already be
	// loaded in process space
	Firefox.Initialize("PR_Write", "nspr4.dll", PR_Write_H);
	// Write jump instruction on original function address
	Firefox.Start();
	return TRUE;
}
 
// our overriding function
DWORD PR_Write_H (DWORD *fd,  void *buf,DWORD amount)
{
	// reset hooks, this will replace the jump instruction to original data
	Firefox.Reset();
        // You may skip the code shown below and call the original function directly
        // since after calling Firefox.Reset() the address of the original function, 
        // now contains the original function's data
        // point prw(function) to original function (optional)
	prw = (prWrite)Firefox.original_function;
        // log the headers
	write_log(log_file, (char*) buf);
	// call the real PR_Write function
	DWORD ret = prw(fd, buf, amount);
	// again place the jump instruction on the original function
	Firefox.Place_Hook();
	return ret;
}

Netfilter hook

This example shows how to use hook to alter network traffic in the Linux kernel using Netfilter.

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/skbuff.h>
 
#include <linux/ip.h>
#include <linux/tcp.h>
#include <linux/in.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>
 
/* Port we want to drop packets on */
static const uint16_t port = 25;
 
/* This is the hook function itself */
static unsigned int hook_func(unsigned int hooknum,
                       struct sk_buff **pskb,
                       const struct net_device *in,
                       const struct net_device *out,
                       int (*okfn)(struct sk_buff *))
{
        struct iphdr *iph = ip_hdr(*pskb);
        struct tcphdr *tcph, tcpbuf;
 
        if (iph->protocol != IPPROTO_TCP)
                return NF_ACCEPT;
 
        tcph = skb_header_pointer(*pskb, ip_hdrlen(*pskb), sizeof(*tcph), &tcpbuf);
        if (tcph == NULL)
                return NF_ACCEPT;
 
        return (tcph->dest == port) ? NF_DROP : NF_ACCEPT;
}
 
/* Used to register our hook function */
static struct nf_hook_ops nfho = {
        .hook     = hook_func,
        .hooknum  = NF_IP_PRE_ROUTING,
        .pf       = NFPROTO_IPV4,
        .priority = NF_IP_PRI_FIRST,
};
 
static __init int my_init(void)
{
        return nf_register_hook(&nfho);
}
 
static __exit void my_exit(void)
{
    nf_unregister_hook(&nfho);
}
 
module_init(my_init);
module_exit(my_exit);

External links

Windows

Linux

Emacs

iOS

See also

References